#include "CharExtracter.h"
#include "IPToolkit.h"
#include "Log.h"
#include "Debug.h"
#include "ml/SampleSpace.h"
#include "ml/KMeans.h"

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>

using namespace std;

bool CharExtracter::divideChar(uchar *data, int iWidth, int iHeight, vector<uchar *> &picList, vector<int> &widthList, vector<int> &heightList) {
    int upside, downside;
    if (preProcess(data, iWidth, iHeight, &upside, &downside) == false) {
        return false;
    }

#ifdef FILTER_NOISE
    //filterNoise(data, iWidth, upside, downside, true);
    filterNoise(data, iWidth, upside, downside, false);
#endif

    int cWidth, cHeight;
    cHeight = downside - upside + 1;

    // horizontal process
    int *histgram = new int[iWidth];

    for (int i = 0; i < iWidth; i++) {
        histgram[i] = 0;
    }

    for (int i = upside; i <= downside; i++) {
        for (int j = 0; j < iWidth; j++) {
            if (*(data + iWidth * i + j) == CHAR) {
                histgram[j]++;
            }
        }
    }

    int dWidth;
    vector<int> cWlist, dWlist, offList;
    // TODO: do we need dWidth?
    if (divideHelp(histgram, iWidth, &cWidth, &dWidth, &offList, &cWlist, &dWlist) == false) {
        delete []histgram;
        return false;
    }
    assert(offList.size() == cWlist.size());

    if (PROPER != scale(cWidth, cHeight, LOOSE)) {
        cWidth = cHeight;
    }

    int size = offList.size(), x, y, cWt, cHt;
    Scale sc;
    bool needBreak = false;
    uchar *alloc = 0;
    for (int count = 0; count < size; count++) {
        cWt = cWlist[count];

        if (cWt < 1) { // ignore block only take one pixel wide
            continue;
        }

        x = offList[count];
        y = upside;
        cHt = cHeight;

        needBreak = false;
        while (!needBreak) {
            findUpAndDown(data, iWidth, &x, &y, &cWt, &cHt);
            sc = scale(cWt, cWidth, CHINESE); // need modofied: only consider Chinese here
            switch (sc) {
                case PROPER:
                    alloc = new uchar[cWt * cHt];
                    copyArea(alloc, data, iWidth, x, y, cWt, cHt);

                    picList.push_back(alloc);
                    widthList.push_back(cWt);
                    heightList.push_back(cHt);

                    needBreak = true;

#ifdef _DEBUG
                    DEBUG_markChar(data, iWidth, iHeight, x, y, cWt, cHt);
#endif

#ifdef DISPLAY_DIVIDED_CHAR
                    DebugToolkit::displayGreyImage(alloc, cWt, cHt);
#endif

                    break;

                case LEFT_TOOBIG:
                    if (cWt < 2 * cWidth) {
                        int *temp = new int[cWt];
                        for (int i = 0; i < cWt; i++) {
                            temp[i] = 0;
                        }

                        for (int i = 0; i < cHt; i++) {
                            for (int j = 0; j < cWt; j++) {
                                if (*(data + iWidth * (y + i) + x + j) == CHAR) {
                                    temp[j]++;
                                }
                            }
                        }

                        int offset = findTroughFromSide(temp, cWt);

                        if (offset <= (cWt + 1) / 2) {
                            x = x + offset + 1;			   
                            cWt = cWt - offset - 1;
			}else if(offset >= cWt){
			    x += cWt - cWidth;
			    cWt = cWidth;
			}else{
			    cWt = cWt - offset - 1;
			}

                        delete[] temp;
                    }else {
                        // ���� x �� x + cWidth �� x + cWt - cWidth �� x + cWt ��ȡ���֣���ȡ���ܵ�ǰ�󲿷�
                        alloc = new uchar[cWidth * cHt];
                        copyArea(alloc, data, iWidth, x, y, cWidth, cHt);

                        picList.push_back(alloc);
                        widthList.push_back(cWidth);
                        heightList.push_back(cHt);
#ifdef _DEBUG
                        DEBUG_markChar(data, iWidth, iHeight, x, y, cWidth, cHt);
#endif

#ifdef DISPLAY_DIVIDED_CHAR
                        DebugToolkit::displayGreyImage(alloc, cWidth, cHt);
#endif

                        alloc = new uchar[cWidth * cHt];
                        copyArea(alloc, data, iWidth, x + cWt - cWidth, y, cWidth, cHt);

                        picList.push_back(alloc);
                        widthList.push_back(cWidth);
                        heightList.push_back(cHt);

#ifdef _DEBUG
                        DEBUG_markChar(data, iWidth, iHeight, x + cWt - cWidth, y, cWidth, cHt);
#endif

#ifdef DISPLAY_DIVIDED_CHAR
                        DebugToolkit::displayGreyImage(alloc, cWidth, cHt);
#endif

                        needBreak = true;
                    }

                    break;

                case LEFT_TOOSMALL:
                    if (!isSign(data, x, y, cWt, cHt)) {
                        if (x + cWidth < iWidth && histgram[x + cWidth + 1] == 0) { // may be overfit by adding 1
                            int i = 1;
                            while (histgram[x + cWidth - i] == 0) {
                                i++;
                            }
                            cWt = cWidth - i + 1;

                            if (scale(cWt, cWidth, CHINESE) != PROPER) {
                                needBreak = true;

                                break;
                            }

                            int tempX, tempY, tempW, tempH;
                            while (count + 1 < size && offList[count + 1] < x + cWt) {
                                tempX = offList[count + 1];
                                tempY = upside;
                                tempW = cWlist[count + 1];
                                tempH = cHeight;

                                findUpAndDown(data, iWidth, &tempX, &tempY, &tempW, &tempH);
                                if (cHt < tempH) {
                                    y = tempY;
                                    cHt = tempH;
                                }

                                count++;
                            }
			    //== [zdm,2008.6.17]
			    //if (cWt<=0 || cHt<=0)
				//return false;
			    //== end
                            alloc = new uchar[cWt * cHt];
                            copyArea(alloc, data, iWidth, x, y, cWt, cHt);

                            picList.push_back(alloc);
                            widthList.push_back(cWt);
                            heightList.push_back(cHt);

#ifdef _DEBUG
                            DEBUG_markChar(data, iWidth, iHeight, x, y, cWt, cHt);
#endif

#ifdef DISPLAY_DIVIDED_CHAR
                            DebugToolkit::displayGreyImage(alloc, cWt, cHt);
#endif

                        }else {
                            // ������ô����
                        }
                    }else {
                        // ������

#ifdef _DEBUG
                        DEBUG_markChar(data, iWidth, iHeight, x, y, cWt, cHt);
#endif
                    }
                    needBreak = true;

                    break;
            }
        }
    }

    delete[] histgram;

    return true;
}

bool CharExtracter::preProcess(const uchar *data, int iWidth, int iHeight, int *upside, int *downside) {
    int threshold = floor(iWidth / iHeight * 1.3f);

    if (horizontalRejectCalc(data, iWidth, iHeight, upside, downside, threshold) == false) {
        return false;
    }

    uchar *newData = new uchar[iWidth * (*downside - *upside + 1)];
    bool found = removeBigConnected(newData, data, iWidth, *upside, *downside);

    if (found) {
        int tempUp, tempDown;

        // TODO: the threshold related parameter 1.1
        if (horizontalRejectCalc(newData, iWidth, *downside - *upside + 1, &tempUp, &tempDown, ceil(iWidth / iHeight * 1.1)) == false) {
            delete[] newData;
            
            return false;
        }

        *upside += tempUp;
        *downside = *upside + tempDown - tempUp;
    }
    delete[] newData;

    return true;
}

bool CharExtracter::horizontalRejectCalc(const uchar *data, int iWidth, int iHeight, int *upside, int *downside, int threshold) {
    int *histagram = new int[iHeight];
    for (int i = 0; i < iHeight; i++) {
        histagram[i] = 0;
    }

    for (int i = 0; i < iHeight; i++) {
        for (int j = 0; j < iWidth; j++) {
            if (*(data + iWidth * i + j) == CHAR) {
                histagram[i]++;
            }
        }
    }

    /*
    cout << "horizontal projection: ";
    for(int i = 0; i<iHeight; i++){
    	cout << histagram[i] << " ";
    }
    cout << endl;
    */

    int up, down, center = iHeight / 2;
    up = down = center; // TODO Ĭ��ͼ��ˮƽ�����߱ش����ַ�
    while (up >= 0 && histagram[up] >= threshold) {
        up--;
    }

    down++;
    while (down < iHeight && histagram[down] >= threshold) {
        down++;
    }

    // TODO: adjust the threshold
    if (down - up <= 10) {   // [liuyi 091014]
        delete []histagram;
        return false;
    }

    *upside = (up >= 0)? up: 0;
    *downside = (down < iHeight)? down: iHeight - 1;

    delete[] histagram;

    return true;
}

bool CharExtracter::removeBigConnected(uchar *dataConn, const uchar *data, int iWidth, int upside, int downside) {
    int cH = downside - upside + 1;

    // TODO: change parameter, looser condition allowed
    int anchor = (cH + 1) / 7;
    /*	cout << "cH: " << cH << " anchor: " << anchor << endl;*/

    anchor = (anchor < 2) ? 2 : anchor;

    // TODO: creating elem each time is unneccessary
    Morphology morp(CHAR, BG);
    Morphology::StructElem elem(anchor*2 + 1, anchor*2 + 1, anchor, anchor);
    int rows = elem.rows;
    int cols = elem.cols;
    uchar *ed = elem.data;
    memset(ed, CHAR, rows*cols*sizeof(uchar));
    for(int i = 0; i < rows; i++) {
        for(int j = 0; j < cols; j++) {
            if(i + j < anchor - 1 || i + j > anchor*3 + 1 || i - j > anchor + 1 || j - i > anchor + 1) {
                *(ed + cols*i + j) = BG;
            }
        }
    }
    
    uchar *dataErode = new uchar[iWidth*cH];
    morp.erode(dataErode, data + iWidth*upside, iWidth, cH, elem);
    
    memcpy(dataConn, data + iWidth*upside, iWidth*cH*sizeof(uchar));
    
    bool found = false;
    for (int i = 0; i < cH; i++) {
        for (int j = 0; j < iWidth; j++) {
            if (*(dataErode + iWidth*i + j) == CHAR) {
                IPToolkit::fillConnetecd(dataErode, iWidth, cH, Point(j, i), BG);

                if (*(dataConn + iWidth*i + j) == CHAR) {
                    IPToolkit::fillConnetecd(dataConn, iWidth, cH, Point(j, i), BG);
                }

                found = true;
            }
        }
    }
    
#ifdef DISPLAY_CONNECTED_IMAGE
    DebugToolkit::displayImage(imageConnected);
#endif

    delete []dataErode;

    return found;
}

CharExtracter::Scale CharExtracter::scale(int fist, int second, InputType type) {
    float scale = fist * 1.0f / second;
    Scale res = PROPER;

    switch (type) {
        case CHINESE:
            if (scale > 1.17) {
                res = LEFT_TOOBIG;
            }else if (scale < 0.733) {
                res = LEFT_TOOSMALL;
            }

            break;

        case ENGLISH:
            if (scale > 1) {
                res = LEFT_TOOBIG;
            }else if (scale < 0.1) {
                res = LEFT_TOOSMALL;
            }

            break;

            // 	case STRICT:
            // 		if(scale > 1.1){
            // 			res = LEFT_TOOBIG;
            // 		}else if(scale < 0.9){
            // 			res = LEFT_TOOSMALL;
            // 		}
            // 
            // 		break;

        case LOOSE:
            if (scale > 1.3) {
                res = LEFT_TOOBIG;
            }else if (scale < 0.7) {
                res = LEFT_TOOSMALL;
            }

            break;

        default:
            assert(false);
    }

    return res;
}

void CharExtracter::findUpAndDown(const uchar *data, int iWidth, int *X, int *Y, int *cWidth, int *cHeight) {
    int xoff = *X, yoff = *Y, i;
    bool isBreak = false;
    for (i = 0; i < *cHeight; i++) {
        for (int j = 0; j < *cWidth; j++) {
            if (*(data + iWidth * (i + yoff) + j + xoff) == CHAR) {
                isBreak = true;

                break;
            }
        }

        if (isBreak) {
            break;
        }
    }
    *Y += i;

    isBreak = false;
    for (i = *cHeight - 1; i >= 0; i--) {
        for (int j = 0; j < *cWidth; j++) {
            if (*(data + iWidth * (i + yoff) + j + xoff) == CHAR) {
                isBreak = true;

                break;
            }
        }

        if (isBreak) {
            break;
        }
    }
    *cHeight = i - *Y + yoff + 1;
}

bool CharExtracter::divideHelp(const int *histagram, int iWidth, int *cW, int *dW, vector<int> *offList, vector<int> *cWlist, vector<int> *dWlist) {
    int i = 0, temp;
    while (i < iWidth && histagram[i] == 0) {
        i++;
    }
    if (i == iWidth) {
        return false;
    }

    while (true) {
        temp = i++;
        while (i < iWidth && histagram[i] > 0) {
            i++;
        }

        offList->push_back(temp);
        cWlist->push_back(i - temp);

        temp = i++;
        while (i < iWidth && histagram[i] == 0) {
            i++;
        }
        if (i >= iWidth) {
            break;
        }
        dWlist->push_back(i - temp);
    }
    *cW = calcMaxFreqValue(cWlist);
    if (dWlist->size()>0){
        *dW = calcMaxFreqValue(dWlist);
    }

    return true;
}

int CharExtracter::calcMaxFreqValue(vector<int> *v) {
    assert(v->size() > 0);

    vector<int> temp(*v);
    sort(temp.begin(), temp.end());

    int value, freq, t, size;
    size = temp.size();
    t = 0;
    freq = 1;
    value = temp[0];
    for (int i = 1; i < size; i++) {
        if (temp[i] != temp[i - 1]) {
            if (i - t >= freq) {
                value = temp[i - 1];
                freq = i - t;
                t = i;
            }
        }
    }
    if (size - t > freq) {
        value = temp[size - 1];
    }

    return value;
}

bool CharExtracter::isSign(const uchar *data, int x, int y, int width, int height) {
    // reserved to check whether this block is a sign
    return false;
}

int CharExtracter::findTroughFromSide(const int *histgram, int len) {
    int head = histgram[0], tail = histgram[len - 1], temp, offset;

    offset = len; // any number big enough
    for (int i = 1; i < (len - 1) / 2; i++) {
        temp = histgram[i];
        if (temp <= histgram[i - 1] && temp <= histgram[i + 1]) {
            offset = i;
            break;
        }
    }

    for (int i = 1; i < (len - 1) / 2; i++) {
        temp = histgram[len - i - 1];
        if (temp <= histgram[len - i - 2] && temp <= histgram[len - i]) {
            if (i < offset) {
                offset = len - i - 1;
            }
            break;
        }
    }

    return offset;
}

void CharExtracter::copyArea(uchar *dest, const uchar *src, int iWidth, int x, int y, int cW, int cH) {
    for (int i = 0; i < cH; i++) {
        memcpy(dest + cW*i, src + iWidth * (y + i) + x, cW);
    }
}

#ifdef _DEBUG

void CharExtracter::DEBUG_markChar(uchar *data, int iWidth, int iHeight, int x, int y, int w, int h) {
    for (int i = 0; i < w; i++) {
        *(data + iWidth * y + x + i) = CHAR;
        *(data + iWidth * (y + h - 1) + x + i) = CHAR;
    }
    for (int i = 0; i < h; i++) {
        *(data + iWidth * (y + i) + x) = CHAR;
        *(data + iWidth * (y + i) + x + w - 1) = CHAR;
    }
}
#endif

void CharExtracter::filterNoise(uchar *data, int iWidth, int upside, int downside, bool strict) {
    int tw = iWidth + 4, iHeight = downside - upside + 1, th = iHeight + 4;
    uchar *pix = NULL, *buff = new uchar[tw * th];

    for (int i = 0; i < 2; i++) {
        memset(buff + tw*i, BG, tw);
        memset(buff + tw * (th - i - 1), BG, tw);
    }
    for (int j = 0; j < 2; j++) {
        for (int i = 0; i < iHeight; i++) {
            *(buff + tw * (i + 2) + j) = BG;
            *(buff + tw * (i + 2) + tw - j - 1) = BG;
        }
    }

    for (int i = 0; i < iHeight; i++) {
        memcpy(buff + tw * (i + 2) + 2, data + iWidth * (i + upside), iWidth);
    }

    int count, noiseCount = 0;
    for (int i = 0; i < iHeight; i++) {
        for (int j = 0; j < iWidth; j++) {
            count = 0;
            pix = buff + tw * (i + 2) + j + 2;

            if (*pix == CHAR) {
                // check four corners
                if (*(pix - 1 - tw) == CHAR) {
                    count++;
                }
                if (*(pix + 1 - tw) == CHAR) {
                    count++;
                }
                if (*(pix + 1 + tw) == CHAR) {
                    count++;
                }
                if (*(pix - 1 + tw) == CHAR) {
                    count++;
                }
                if (*(pix - 1 + tw) == CHAR) {
                    count++;
                }

                if (count > 2) {
                    continue;
                }

                if (count < 2) {
                    // * * *
                    // * 1 *
                    // * * *
                    if (*(pix - tw) == CHAR) {
                        count++;
                    }
                    if (*(pix - 1) == CHAR) {
                        count++;
                    }
                    if (*(pix + tw) == CHAR) {
                        count++;
                    }
                    if (*(pix + 1) == CHAR) {
                        count++;
                    }

                    if (count == 0) {
                        *(data + iWidth * (i + upside) + j) = *pix = BG;
                        noiseCount++;

                        continue;
                    }else if (count > 1 || !strict) {
                        continue;
                    }else {

                        // * * * *
                        // * * 1 *
                        // * * * *
                        count = 0;

                        for (int k = 0; k < 4; k++) {
                            if (*(pix - 2 - tw + k) == CHAR) {
                                count++;
                            }
                            if (*(pix - 2 + tw + k) == CHAR) {
                                count++;
                            }
                        }
                        if ((*(pix - 2) == CHAR)) {
                            count++;
                        }
                        if (*(pix + 1) == CHAR) {
                            count++;
                        }

                        if (count > 1) {
                            continue;
                        }

                        // * * *
                        // * 1 *
                        // * * *
                        // * * *
                        count = 0;

                        for (int k = 0; k < 4; k++) {
                            if (*(pix - 1 + tw * (k - 1)) == CHAR) {
                                count++;
                            }
                            if (*(pix + 1 + tw * (k - 1)) == CHAR) {
                                count++;
                            }
                        }
                        if ((*(pix - tw) == CHAR)) {
                            count++;
                        }
                        if (*(pix + tw * 2) == CHAR) {
                            count++;
                        }

                        if (count > 1) {
                            continue;
                        }


                        // * * * *
                        // * 1 * *
                        // * * * *
                        count = 0;

                        for (int k = 0; k < 4; k++) {
                            if (*(pix - 1 - tw + k) == CHAR) {
                                count++;
                            }
                            if (*(pix - 1 + tw + k) == CHAR) {
                                count++;
                            }
                        }
                        if ((*(pix - 1) == CHAR)) {
                            count++;
                        }
                        if (*(pix + 2) == CHAR) {
                            count++;
                        }

                        if (count > 1) {
                            continue;
                        }
                    }
                }

                // * * * *
                // * 1 * *
                // * * * *
                // * * * *
                count = 0;

                for (int k = 0; k < 4; k++) {
                    if (*(pix - 1 - tw + k) == CHAR) {
                        count++;
                    }
                    if (*(pix - 1 + tw * 2 + k) == CHAR) {
                        count++;
                    }
                }

                if (count > 1) {
                    continue;
                }

                for (int k = 0; k < 2; k++) {
                    if (*(pix - 1 + tw * k) == CHAR) {
                        count++;
                    }
                    if (*(pix + 2 + tw * k) == CHAR) {
                        count++;
                    }
                }

                if (count > 1) {
                    continue;
                }

                // * * * *
                // * * 1 *
                // * * * *
                // * * * *
                count = 0;

                for (int k = 0; k < 4; k++) {
                    if (*(pix - 2 - tw + k) == CHAR) {
                        count++;
                    }
                    if (*(pix - 2 + tw * 2 + k) == CHAR) {
                        count++;
                    }
                }

                if (count > 1) {
                    continue;
                }

                for (int k = 0; k < 2; k++) {
                    if (*(pix - 2 + tw * k) == CHAR) {
                        count++;
                    }
                    if (*(pix + 1 + tw * k) == CHAR) {
                        count++;
                    }
                }

                if (count < 2) {
                    *(data + iWidth * (i + upside) + j) = *pix = BG;
                    noiseCount++;

                    if (*(pix + 1) == CHAR) {
                        *(data + iWidth * (i + upside) + j + 1) = *(pix + 1) = BG;
                        noiseCount++;

                        continue;
                    }
                    if (*(pix - 1 + tw) == CHAR) {
                        *(data + iWidth * (i + 1 + upside) + j - 1) = *(pix - 1 + tw) = BG;
                        noiseCount++;

                        continue;
                    }
                    if (*(pix + tw) == CHAR) {
                        *(data + iWidth * (i + 1 + upside) + j) = *(pix + tw) = BG;
                        noiseCount++;
                    }
                }
            }
        }
    }

    //cout << "noise count: " << noiseCount << endl;

    delete[] buff;
}

void CharExtracter::binarize(uchar *dst, const uchar *src, int width, int height) {
    int size = width*height;

    SampleSpace ss(1, Distance::EUCLIDEAN);

    vector<Sample *> sampleList;
    ss.newSampleArray(sampleList, size);
    for(int i = 0; i < size; i++) {
        sampleList.at(i)->assignFeature(0, src[i]);
    }

    const int K = 3;
    vector<Sample *> init;
    ss.newSampleArray(init, K);
    init.at(0)->assignFeature(0, 0);
    init.at(1)->assignFeature(0, 125);
    init.at(2)->assignFeature(0, 255);

    KMeans kmeans(ss, K, init);

    ss.cluster(kmeans, sampleList);

    float data;
    float min = 255, max = 0;
    for(int i = 0; i < size; i++) {
        if(sampleList.at(i)->getClass() == 1) {
            data = *sampleList.at(i)->getFeature();

            if(data < min) {
                min = data;
            }
            
            if(data > max) {
                max = data;
            }
        }
    }

    // TODO: if foreground is easily distinguished from background, the bigger factor, the better.
    // Otherwise, factor is better to be as small as 2.
    const int factor = 3;
    float piece = (max - min)/factor;

    float threshold = max - piece;
    for(int i = 0; i < size; i++) {
        switch(sampleList.at(i)->getClass()) {
            case 2:
                dst[i] = CHAR;
                break;

            case 1:
                data = *sampleList.at(i)->getFeature();
                dst[i] = (data > threshold)? CHAR: BG;
                break;

            default:
                dst[i] = BG;
        }
    }

    bool result = check(dst, width, height);
    if (result == false) {
        threshold = min + piece;
        for (int i = 0; i < size; i++) {
            switch (sampleList.at(i)->getClass()) {
                case 0:
                    dst[i] = CHAR;
                    break;

                case 1:
                    data = *sampleList.at(i)->getFeature();
                    dst[i] = (data < threshold) ? CHAR : BG;
                    break;

                default:
                    dst[i] = BG;
            }
        }
    }

/* not necessary, for foreground color is usaully light or dark
    if (result == false) {
        result = check(dst, width, height);
        if (result == false) {
            for (int i = 0; i < size; i++) {
                switch (sampleList.at(i)->getClass()) {
                    case 1:
                        dst[i] = CHAR;
                        break;

                    default:
                        dst[i] = BG;
                }
            }
        }
    }
*/

    // remvoe connected regions with foreground color located at sides
    for (int i = 0; i < width; i++) {
        if (*(dst + i) == CHAR) {
            IPToolkit::fillConnetecd(dst, width, height, Point(i, 0), BG);
        }

        if (*(dst + width * (height - 1) + i) == CHAR) {
            IPToolkit::fillConnetecd(dst, width, height, Point(i, height - 1), BG);
        }
    }
    for (int i = 0; i < height; i++) {
        if (*(dst + width*i) == CHAR) {
            IPToolkit::fillConnetecd(dst, width, height, Point(0, i), BG);
        }

        if (*(dst + width*(i + 1) - 1) == CHAR) {
            IPToolkit::fillConnetecd(dst, width, height, Point(width-1, i), BG);
        }
    }

#ifdef SHOW_BINARIZED_IMAGE
    Debug::showImageData(dst, width, height);
#endif

    ss.deleteSampleArray(sampleList);
    ss.deleteSampleArray(init);
}

bool CharExtracter::check(const uchar *greyData, int width, int height) {
    int fg = 0;

    const uchar *p = greyData;
    const uchar *end = greyData + width*height;
    for( ; p < end; p++) {
        if(*p == CHAR) {
            fg++;
        }
    }

    float value = fg*1.0f/(width*height);
    return value < 0.5f && value > 0.1f;
}
